Skip to main content

dash-social-signin

Social sign-in for Dash, made simple. Drop-in OAuth buttons and server-side helpers for authorization and token verification.

Supported providers

Google · Facebook · GitHub · X (Twitter) · LinkedIn · Microsoft · Apple · Discord · Slack

Why use it?

Built for Dash — Most OAuth libraries are generic and don't account for Dash's layout system or asset pipeline. This package is built specifically for Dash.

One consistent API for all providers — Every provider has quirks. Facebook uses a different token endpoint format. X requires PKCE. Apple requires a JWT as the client secret. This package smooths all of that over so you call the same functions regardless of provider.

Security built in — PKCE prevents authorization code interception. A state token is generated per login attempt, stored server-side, and verified on callback. Tokens are never exposed to the browser — all exchange happens on your Flask/Dash backend.

Flexible — Use the built-in build_container() for a ready-made UI, or skip it entirely and build your own buttons. The backend helpers work either way.


Installation

pip install dash-social-signin

Store your provider credentials in a .env file. Use .env.example as the template. See docs/PROVIDERS.md for step-by-step credential setup for all 9 providers.


Quick Start

The setup is split into three sections depending on your integration needs:

  • Section A — Using the default pre-styled buttons
  • Section B — Building your own custom buttons
  • Section C — The mandatory backend routes (required by both A and B)

Section A — Using the Default Buttons

The package ships ready-to-use styled OAuth buttons. Two steps to get them into your layout.

Step 1 — Copy assets into your app

from dash_social_signin import install_assets

install_assets("./assets")

Step 2 — Mount the container in your layout

import os
from dash import Dash, html
from dash_social_signin import build_container
from dotenv import load_dotenv

load_dotenv()

app = Dash(__name__)
app.server.secret_key = os.environ.get("DASH_SOCIAL_SIGNIN_SECRET", "change-me")

BASE_URL = os.environ.get("BASE_URL", "http://localhost:8050")

app.layout = html.Div([
    build_container(
        {
            "providers": {
                "google": {
                    "clientId": os.environ.get("GOOGLE_CLIENT_ID"),
                    "redirectUri": f"{BASE_URL}/auth/callback?provider=google",
                    "authUrl": f"{BASE_URL}/auth/start",
                    "extraParams": {"provider": "google"},
                    "scope": "openid email profile",
                },
                "github": {
                    "clientId": os.environ.get("GITHUB_CLIENT_ID"),
                    "redirectUri": f"{BASE_URL}/auth/callback?provider=github",
                    "authUrl": f"{BASE_URL}/auth/start",
                    "extraParams": {"provider": "github"},
                    "scope": "read:user user:email",
                },
            }
        },
        id="social-signin",
    )
])

The JS renders the buttons into the container automatically. authUrl points to your /auth/start backend route — see Section C.


Section B — Building Custom Buttons

You can skip install_assets() and build_container() entirely and design your own layout. The only requirement is that your buttons link to /auth/start with the right query parameters.

This example uses dash-bootstrap-components and Font Awesome:

import os
import dash
import dash_bootstrap_components as dbc
from dash import html
from dotenv import load_dotenv

load_dotenv()

app = dash.Dash(
    __name__,
    external_stylesheets=[dbc.themes.BOOTSTRAP, dbc.icons.FONT_AWESOME]
)
app.server.secret_key = os.environ.get("DASH_SOCIAL_SIGNIN_SECRET", "change-me")

BASE_URL = os.environ.get("BASE_URL", "http://localhost:8050")

app.layout = html.Div([
    html.H3("Sign in to your account"),
    html.A(
        dbc.Button(
            [html.I(className="fab fa-google me-2"), "Continue with Google"],
            color="danger",
            outline=True,
            className="d-flex align-items-center px-4 py-2",
        ),
        href=f"/auth/start?provider=google&scope=openid email profile&redirectUri={BASE_URL}/auth/callback?provider=google",
        style={"textDecoration": "none"},
    )
], className="d-flex flex-column align-items-center justify-content-center vh-100")

Any button or link that hits /auth/start?provider=<name>&scope=<scopes> will work.


Section C — The Mandatory Backend Routes

Both Section A and Section B rely on the same two server-side routes. These handle state generation, PKCE, and token exchange.

1. /auth/start — Initiate the login

Generates a PKCE verifier and a CSRF state token, stores them in the session, then redirects the user to the provider.

import os
import secrets
from flask import redirect, request, session
from dash_social_signin import build_authorize_url, build_pkce_challenge, build_pkce_verifier
from dash_social_signin.oauth import PROVIDER_CONFIG

BASE_URL = os.environ.get("BASE_URL", "http://localhost:8050")

@app.server.route("/auth/start")
def auth_start():
    provider = request.args.get("provider")
    if not provider or provider not in PROVIDER_CONFIG:
        return "Invalid provider", 400

    state = secrets.token_urlsafe(16)
    session[f"oauth_state:{provider}"] = state

    verifier = build_pkce_verifier()
    session[f"pkce_verifier:{provider}"] = verifier
    challenge = build_pkce_challenge(verifier)

    auth_url = build_authorize_url(
        provider=provider,
        client_id=os.environ.get(f"{provider.upper()}_CLIENT_ID"),
        redirect_uri=f"{BASE_URL}/auth/callback?provider={provider}",
        scope=request.args.get("scope"),
        state=state,
        code_challenge=challenge,
    )

    return redirect(auth_url)

2. /auth/callback — Handle the provider redirect

Validates the state, exchanges the authorization code for tokens, and fetches the user's profile.

import os
from flask import jsonify, request, session
from dash_social_signin import verify_oauth_callback

BASE_URL = os.environ.get("BASE_URL", "http://localhost:8050")

@app.server.route("/auth/callback", methods=["GET", "POST"])
def auth_callback():
    # Apple sends a POST; all other providers send a GET
    get_param = lambda k: request.args.get(k) or request.form.get(k)

    provider = get_param("provider")
    code = get_param("code")
    if not provider or not code:
        return "Missing provider or code", 400

    # CSRF check
    returned_state = get_param("state")
    expected_state = session.pop(f"oauth_state:{provider}", None)
    if expected_state and returned_state != expected_state:
        return "Invalid state", 400

    code_verifier = session.pop(f"pkce_verifier:{provider}", None)

    tokens, userinfo = verify_oauth_callback(
        provider=provider,
        code=code,
        redirect_uri=f"{BASE_URL}/auth/callback?provider={provider}",
        client_id=os.environ.get(f"{provider.upper()}_CLIENT_ID"),
        client_secret=os.environ.get(f"{provider.upper()}_CLIENT_SECRET"),
        code_verifier=code_verifier,
    )

    # At this point you have the user's profile.
    # Save to your database, set a session cookie, redirect to your dashboard.
    return jsonify({"provider": provider, "userinfo": userinfo})

Security features

PKCE — Before redirecting the user, a random verifier is generated and stored server-side. A SHA-256 hash of it (the challenge) is sent to the provider. When the code comes back, the verifier is sent with the token request. The provider checks they match — an intercepted code is useless without the verifier.

CSRF protection via state — A random token is generated per login attempt and stored in the session. The provider echoes it back in the callback. If it doesn't match, the request is rejected.

Tokens stay on the server — The browser never sees your client secret or access tokens. In production, store tokens in your database and give the user a session cookie — never return raw tokens to the frontend.


Running your app

import os

if __name__ == "__main__":
    port = int(os.environ.get("PORT", 8050))
    app.run(host="0.0.0.0", port=port, debug=True)

Deployment

Set these environment variables before deploying (Use .env.example as the template):

BASE_URL=https://your-domain.com
DASH_SOCIAL_SIGNIN_SECRET=a-long-random-string
GOOGLE_CLIENT_ID=...
GOOGLE_CLIENT_SECRET=...
# etc.

Register your callback URL in each provider's developer console. The pattern is always:

https://your-domain.com/auth/callback?provider=PROVIDER_NAME

For local development replace with http://localhost:8050.

Provider Redirect URI
Google https://your-domain.com/auth/callback?provider=google
Facebook https://your-domain.com/auth/callback?provider=facebook
GitHub https://your-domain.com/auth/callback?provider=github
X https://your-domain.com/auth/callback?provider=x
LinkedIn https://your-domain.com/auth/callback?provider=linkedin
Microsoft https://your-domain.com/auth/callback?provider=microsoft
Apple https://your-domain.com/auth/callback?provider=apple
Discord https://your-domain.com/auth/callback?provider=discord
Slack https://your-domain.com/auth/callback?provider=slack

What this package does NOT do

It handles OAuth. What you do after that — saving users to a database, managing sessions, handling logout, registration flows — is up to you. This is intentional. Every app has different requirements, and this package shouldn't force a database or session library on you.

After verify_oauth_callback() returns userinfo, you have the user's name, email, and profile picture. The rest is your app's business logic.


Example app

See examples/app.py for a full runnable demo covering all 9 providers.

pip install "dash-social-signin[examples]"
python examples/app.py

Provider credential setup

Full step-by-step setup guides for all 9 providers: docs/PROVIDERS.md


Connect and contribute

License

MIT

Release files for dash-social-signin 0.1.3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for dash-social-signin 0.1.3
File Size Uploaded
dash_social_signin-0.1.3.tar.gz 13.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for dash-social-signin 0.1.3
File Interpreter ABI Platform
dash_social_signin-0.1.3-py3-none-any.whl Python 3 none any Details

Total release size: 24.6 kB

Release files / dash_social_signin-0.1.3.tar.gz

Download URL dash_social_signin-0.1.3.tar.gz
Size 13.7 kB
Tags Source
SHA-256 checksum
How to use checksums
21ae114fa21c8ac7e60665e55cc416e7db29bea3492234d6383753082af08234
BLAKE2b-256 checksum
How to use checksums
e03f897c69762055a596e65124491c77fd2e2af799b0fa975c5ac53aa135ea2f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.2

Release files / dash_social_signin-0.1.3-py3-none-any.whl

Download URL dash_social_signin-0.1.3-py3-none-any.whl
Size 10.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
b71787559a20f139fbc7dfefd3c25f1ca1d70c717a84fd5669b2faf2c6bd0e5e
BLAKE2b-256 checksum
How to use checksums
3ee23d09d7129a7e45e82b3484919ddb07f8aaadd2965a792c1cd2a8ef7b7059
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.2

Release history Release notifications | RSS feed

This release

0.1.3 This release

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page